define(function (require, exports, module) {
    const {DrawPolygon} = require('./DrawPolygon.js').onload();
    const Constants = require('./utils/constants.js').onload();
    const createVertex = require('./utils/create_vertex.js').onload();
    const { getPixelBetweenTowPointsWithZoom } = require('./utils/pixelUtils.js').onload();
    const doubleClickZoom = require('./utils/double_click_zoom.js').onload();

    const ElMessage = window.ELEMENT.Message
    const cursors = Constants.cursors
    const booleanIntersects = turf.booleanIntersects

    function onload() {

        const PolygonStickTo = Object.assign({}, DrawPolygon)

        const PolygonStickToEvents = Object.assign({
            UPDATE_POINT_A: 'draw.update.point.a',
            UPDATE_POINT_B: 'draw.update.point.b'
        }, Constants.events)

        // 当前吸附点
        const currentStickToPointId = "current-stick-to";
        // 最小像素允许, 低于这个像素视为同一位置
        const minPixelAllow = 20

        /**
         * 启动函数
         * @param opts: {
         *   coordinatesArr: 已点击的坐标数组（非必填）
         *   eventType: 事件类型（必填）
         *   self: 自身网格（MultiPolygon类型的Feature）（必填）
         *   others: 其他网格（全是MultiPolygon的FeatureCollection），包括：brothers，parent（必填）
         * }
         */
        PolygonStickTo.onSetup = function (opts) {
            const polygon = this.newFeature({
                type: Constants.geojsonTypes.FEATURE,
                properties: {},
                geometry: {
                    type: Constants.geojsonTypes.POLYGON,
                    coordinates: [[]]
                }
            })
            this.addFeature(polygon)

            this.clearSelectedFeatures()
            doubleClickZoom.disable(this)
            this.updateUIClasses({mouse: Constants.cursors.ADD});
            this.activateUIButton(Constants.types.POLYGON)
            this.setActionableState({
                trash: true
            })
            if (opts.coordinatesArr) {
                const length = opts.coordinatesArr.length;
                for (let i = 0; i < length - 1; i++) {
                    polygon.updateCoordinate(`0.${i}`, opts.coordinatesArr[i][0], opts.coordinatesArr[i][1])
                    polygon.updateCoordinate(`0.${i + 1}`, opts.coordinatesArr[i][0], opts.coordinatesArr[i][1])
                }
                polygon.updateCoordinate(`0.${length - 1}`, opts.coordinatesArr[length - 1][0], opts.coordinatesArr[length - 1][1])
            }

            return {
                eventType: opts.eventType,
                others: opts.others,
                self: opts.self,
                polygon,
                currentVertexPosition: opts.coordinatesArr ? opts.coordinatesArr.length - 1 : 0
            }
        }

        /**
         * 在地图上显示特征
         * @param state 状态
         * @param geojson 图形数据
         * @param display 用于显示的回调函数
         * @returns {*}
         */
        PolygonStickTo.toDisplayFeatures = function (state, geojson, display) {
            const isActivePolygon = geojson.properties.id === state.polygon.id
            geojson.properties.active = (isActivePolygon) ? Constants.activeStates.ACTIVE : Constants.activeStates.INACTIVE
            if (!isActivePolygon) return display(geojson)

            // Don't render a polygon until it has two positions
            // (and a 3rd which is just the first repeated)
            if (geojson.geometry.coordinates.length === 0) return

            const coordinateCount = geojson.geometry.coordinates[0].length
            // 2 coordinates after selecting a draw type
            // 3 after creating the first point
            if (coordinateCount < 3) {
                return
            }
            geojson.properties.meta = Constants.meta.FEATURE
            display(createVertex(state.polygon.id, geojson.geometry.coordinates[0][0], '0.0', false))
            if (coordinateCount > 3) {
                // Add a start position marker to the map, clicking on this will finish the feature
                // This should only be shown when we're in a valid spot
                const endPos = geojson.geometry.coordinates[0].length - 3
                display(createVertex(state.polygon.id, geojson.geometry.coordinates[0][endPos], `0.${endPos}`, false))
            }
            if (coordinateCount <= 4) {
                // If we've only drawn two positions (plus the closer),
                // make a LineString instead of a Polygon
                const lineCoordinates = [
                    [geojson.geometry.coordinates[0][0][0], geojson.geometry.coordinates[0][0][1]], [geojson.geometry.coordinates[0][1][0], geojson.geometry.coordinates[0][1][1]]
                ]
                // create an initial vertex so that we can track the first point on mobile devices
                display({
                    type: Constants.geojsonTypes.FEATURE,
                    properties: geojson.properties,
                    geometry: {
                        coordinates: lineCoordinates,
                        type: Constants.geojsonTypes.LINE_STRING
                    }
                })
                if (coordinateCount === 3) {
                    return
                }
            }
            // render the Polygon
            return display(geojson)
        }

        /**
         * 获取当前信息
         * @param e 事件
         * @param state 状态
         * @param isClick 是否为点击
         * @returns {{currentCoordinate: *, endVertexCoordinate: *, startVertexCoordinate: *}}
         */
        function getCurrentInfo(e, state, isClick) {
            // 多边形坐标
            const polygonCoordinates = state.polygon.coordinates[0]
            // 当前新点击的点位在多边形中的下标
            const startVertexCoordinate = polygonCoordinates[0]
            const endVertexCoordinate = polygonCoordinates[state.currentVertexPosition - 1]
            // 当前新点击的点位坐标
            const {currentCoordinate, onBoundary} = adjustCurrentCoordinate(state, isClick, endVertexCoordinate, [e.lngLat.lng, e.lngLat.lat], state.self, state.others, this.map.getZoom())
            return {currentCoordinate, onBoundary, startVertexCoordinate, endVertexCoordinate}
        }

        /**
         * 鼠标移动事件
         * @param state 状态
         * @param e 事件
         */
        PolygonStickTo.onMouseMove = function (state, e) {
            const {
                currentCoordinate,
                startVertexCoordinate,
                endVertexCoordinate
            } = getCurrentInfo.call(this, e, state, false)
            const polygon = state.polygon;
            polygon.updateCoordinate(`0.${state.currentVertexPosition}`, currentCoordinate[0], currentCoordinate[1])
            // 鼠标移动吸附时，显示一个明显的点
            const source = this.map.getSource(currentStickToPointId);
            if (source) {
                source.setData({
                    type: Constants.geojsonTypes.POINT,
                    coordinates: currentCoordinate
                })
            }
            if (state.currentVertexPosition > 0 && isVertex(currentCoordinate, startVertexCoordinate, endVertexCoordinate, this.map.getZoom())) {
                this.updateUIClasses({mouse: Constants.cursors.POINTER})
            } else {
                this.updateUIClasses({mouse: Constants.cursors.ADD})
            }
            this.map.fire(state.eventType, {coordinatesArr: polygon.coordinates[0]})
        }

        /**
         * 点击事件
         * @type {PolygonStickTo.onClick}
         */
        PolygonStickTo.onTap = PolygonStickTo.onClick = function (state, e) {
            const {
                currentCoordinate,
                onBoundary,
                startVertexCoordinate,
                endVertexCoordinate
            } = getCurrentInfo.call(this, e, state, true)
            if (state.currentVertexPosition > 0 && isVertex(currentCoordinate, startVertexCoordinate, endVertexCoordinate, this.map.getZoom())) {
                clearCurrentStickToPoint.call(this);
                return this.changeMode(Constants.modes.SIMPLE_SELECT, {featureIds: [state.polygon.id]})
            } else {
                return this.clickAnywhere(state, currentCoordinate, onBoundary)
            }
        }

        /**
         * 非顶点的点击处理
         * @param state 状态
         * @param currentCoordinate 当前坐标
         * @param onBoundary 是否在边界上
         * @returns {boolean}
         */
        PolygonStickTo.clickAnywhere = function (state, currentCoordinate, onBoundary) {
            // 多边形
            const polygon = state.polygon
            // 多边形顶点坐标
            const polygonCoordinates = []
            // 浅克隆, 防止后面对坐标数组的操作, 影响原有数组
            Object.assign(polygonCoordinates, polygon.coordinates[0])

            this.updateUIClasses({mouse: cursors.ADD})

            const currentPoint = turf.point(currentCoordinate);

            // 在自身多面内部，且不在边界线上
            if (state.self && turf.booleanPointInPolygon(currentPoint, state.self, {ignoreBoundary: true})) {
                ElMessage.error('不能在自身网格之上再绘制网格! ')
                return false
            }

            // 包括：parent、brothers
            const otherFeatures = state.others.features
            for (let i = 0; i < otherFeatures.length; i++) {
                const otherFeature = otherFeatures[i]
                if (!onBoundary && otherFeature.properties.type === "parent" && !turf.booleanPointInPolygon(currentPoint, otherFeature, {ignoreBoundary: false})) {
                    ElMessage.error('不能在父网格之外绘制网格! ')
                    return false
                }
                if (!onBoundary && otherFeature.properties.type === "brothers" && turf.booleanPointInPolygon(currentPoint, otherFeature, {ignoreBoundary: true})) {
                    ElMessage.error('不能在同级网格之上绘制网格! ')
                    return false
                }
            }

            // if (crossSelf(polygonCoordinates, currentIndex, currentCoordinate)) {
            //     ElMessage.error('不能和自己有交叉! ')
            //     return false
            // } else {
            polygon.updateCoordinate(`0.${state.currentVertexPosition}`, currentCoordinate[0], currentCoordinate[1])
            polygon.updateCoordinate(`0.${++state.currentVertexPosition}`, currentCoordinate[0], currentCoordinate[1])
            this.map.fire(state.eventType, {coordinatesArr: polygon.coordinates[0]})
            // }
        }

        /**
         * 结束时，删除一个吸附点
         */
        function clearCurrentStickToPoint() {
            const source = this.map.getSource(currentStickToPointId);
            if (source) {
                source.setData({
                    type: "FeatureCollection",
                    features: []
                })
            }
        }

        /**
         * 结束处理
         * @param state 状态
         */
        DrawPolygon.onStop = function (state) {
            this.updateUIClasses({mouse: Constants.cursors.NONE});
            doubleClickZoom.enable(this);
            this.activateUIButton();

            // check to see if we've deleted this feature
            if (this.getFeature(state.polygon.id) === undefined) return;

            //remove last added coordinate
            state.polygon.removeCoordinate(`0.${state.currentVertexPosition}`);
            if (state.polygon.isValid()) {
                this.map.fire(Constants.events.CREATE, {
                    features: [state.polygon.toGeoJSON()]
                });
            } else {
                this.deleteFeature([state.polygon.id], {silent: true});
                this.changeMode(Constants.modes.SIMPLE_SELECT, {}, {silent: true});
            }
            clearCurrentStickToPoint.call(this);
        };

        /**
         * 重写是否为顶点判断
         * @param currentPointCoordinate 当前点坐标
         * @param startVertexCoordinate  起始顶点坐标
         * @param endVertexCoordinate    结束顶点坐标
         * @param zoomLevel 层级
         */
        const isVertex = (currentPointCoordinate, startVertexCoordinate, endVertexCoordinate, zoomLevel) => {
            let isVertex = false
            if (startVertexCoordinate) {
                isVertex = isVertex || (getPixelBetweenTowPointsWithZoom(currentPointCoordinate, startVertexCoordinate, zoomLevel) < minPixelAllow)
            }
            if (endVertexCoordinate) {
                isVertex = isVertex || (getPixelBetweenTowPointsWithZoom(currentPointCoordinate, endVertexCoordinate, zoomLevel) < minPixelAllow)
            }
            return isVertex
        }

        /**
         * 是否为相同的点
         * @param pointA    pointA的Feature
         * @param pointB    pointB的Feature
         * @returns {boolean}
         */
        const isSamePoint = (pointA, pointB) => {
            // 单位：两点间的距离小于这个值，视为同一个点 miles
            const miniLength = 0.00003
            return turf.distance(pointA, pointB, {units: 'miles'}) < miniLength
        }

        /**
         * 矫正当前坐标: 当前点在地图上的任何一个已有多边形上, 则矫正为这个多边形上距离其最近的点
         * @param state 状态
         * @param isClick 是否为点击
         * @param endVertexCoordinate 上一个顶点
         * @param currentCoordinate 当前坐标
         * @param self 自身网格（MultiPolygon类型的Feature）
         * @param others 其他网格（全是MultiPolygon的FeatureCollection）
         * @param zoomLevel 层级
         * @returns {*}
         */
        const adjustCurrentCoordinate = (state, isClick, endVertexCoordinate, currentCoordinate, self, others, zoomLevel) => {
            const stickToFeatures = others.features
            if (self) stickToFeatures.push(self)

            function getLine1(start, splitCoordinates1, splitCoordinates2) {
                let line
                if (isSamePoint(start, turf.point(splitCoordinates1[0]))) {
                    line = turf.lineString(splitCoordinates1)
                } else if (isSamePoint(start, turf.point(splitCoordinates1[splitCoordinates1.length - 1]))) {
                    // 逆转顺序
                    line = turf.lineString(splitCoordinates1.reverse())
                } else if (isSamePoint(start, turf.point(splitCoordinates2[0]))) {
                    line = turf.lineString(splitCoordinates2)
                } else if (isSamePoint(start, turf.point(splitCoordinates2[splitCoordinates2.length - 1]))) {
                    line = turf.lineString(splitCoordinates2.reverse())
                }
                return line;
            }

            function getLine2(stop, splitCoordinates1, splitCoordinates2) {
                let line
                if (isSamePoint(stop, turf.point(splitCoordinates1[0]))) {
                    line = turf.lineString(splitCoordinates1.reverse())
                } else if (isSamePoint(stop, turf.point(splitCoordinates1[splitCoordinates1.length - 1]))) {
                    // 逆转顺序
                    line = turf.lineString(splitCoordinates1)
                } else if (isSamePoint(stop, turf.point(splitCoordinates2[0]))) {
                    line = turf.lineString(splitCoordinates2.reverse())
                } else if (isSamePoint(stop, turf.point(splitCoordinates2[splitCoordinates2.length - 1]))) {
                    line = turf.lineString(splitCoordinates2)
                }
                return line;
            }

            /**
             * 点是线的起点和终点
             * @param start 起点
             * @param stop  终点
             * @param coordinates 线坐标
             * @returns {boolean}
             */
            function isVertexOfLineInOrder(start, stop, coordinates) {
                return isSamePoint(start, turf.point(coordinates[0])) && isSamePoint(stop, turf.point(coordinates[coordinates.length - 1]));
            }

            function stickToMore(ring, coordinate) {
                if (isClick && endVertexCoordinate) {
                    if (turf.booleanPointOnLine(turf.point(endVertexCoordinate), ring)) {
                        // endVertexCoordinate：起点（可能不是线的顶点）
                        const start = turf.point(endVertexCoordinate);
                        // coordinate：终点(必定是线的顶点)
                        const stop = turf.point(coordinate);
                        // 1. 对线进行切割（乱序）
                        const splitter = turf.multiPolygon(
                            [turf.circle(endVertexCoordinate, 0.00001, {steps: 10, units: 'miles'}).geometry.coordinates,
                                turf.circle(coordinate, 0.00001, {steps: 10, units: 'miles'}).geometry.coordinates]);
                        const splitFeatureCollection = turf.lineSplit(ring, splitter).features;

                        // 2. 整理出折点顺序正确的2条线
                        let line1, line2
                        if (splitFeatureCollection.length === 2) {
                            const splitCoordinates1 = splitFeatureCollection[0].geometry.coordinates;
                            const splitCoordinates2 = splitFeatureCollection[1].geometry.coordinates;
                            line1 = getLine1(start, splitCoordinates1, splitCoordinates2);
                            line2 = getLine2(stop, splitCoordinates1, splitCoordinates2);
                        } else {
                            // 取最长的三个
                            splitFeatureCollection.sort(function(a,b){return turf.length(b, {units: 'miles'}) - turf.length(a, {units: 'miles'})});
                            const splitCoordinates1 = splitFeatureCollection[0].geometry.coordinates;
                            const splitCoordinates2 = splitFeatureCollection[1].geometry.coordinates;
                            const splitCoordinates3 = splitFeatureCollection[2].geometry.coordinates;
                            let lineNeed1, lineNeed2;
                            if (isVertexOfLineInOrder(start, stop, splitCoordinates1)) {
                                line1 = turf.lineString(splitCoordinates1)
                                lineNeed1 = splitCoordinates2
                                lineNeed2 = splitCoordinates3
                            } else if (isVertexOfLineInOrder(stop, start, splitCoordinates1)) {
                                line1 = turf.lineString(splitCoordinates1.reverse())
                                lineNeed1 = splitCoordinates2
                                lineNeed2 = splitCoordinates3
                            } else if (isVertexOfLineInOrder(start, stop, splitCoordinates2)) {
                                line1 = turf.lineString(splitCoordinates2)
                                lineNeed1 = splitCoordinates1
                                lineNeed2 = splitCoordinates3
                            } else if (isVertexOfLineInOrder(stop, start, splitCoordinates2)) {
                                line1 = turf.lineString(splitCoordinates2.reverse())
                                lineNeed1 = splitCoordinates1
                                lineNeed2 = splitCoordinates3
                            } else if (isVertexOfLineInOrder(start, stop, splitCoordinates3)) {
                                line1 = turf.lineString(splitCoordinates3)
                                lineNeed1 = splitCoordinates1
                                lineNeed2 = splitCoordinates2
                            } else {
                                line1 = turf.lineString(splitCoordinates3.reverse())
                                lineNeed1 = splitCoordinates1
                                lineNeed2 = splitCoordinates2
                            }

                            let finalLine2Coordinates = []
                            if (isSamePoint(start, turf.point(lineNeed1[0]))) {
                                if (isSamePoint(stop, turf.point(lineNeed2[0]))) {
                                    const lineNeed = lineNeed2.reverse()
                                    lineNeed.unshift()
                                    finalLine2Coordinates = [...lineNeed1, ...lineNeed]
                                } else {
                                    lineNeed2.unshift()
                                    finalLine2Coordinates = [...lineNeed1, ...lineNeed2]
                                }
                            } else if (isSamePoint(start, turf.point(lineNeed1[lineNeed1.length - 1]))) {
                                if (isSamePoint(stop, turf.point(lineNeed2[0]))) {
                                    const lineNeed = lineNeed2.reverse()
                                    lineNeed.unshift()
                                    finalLine2Coordinates = [...lineNeed1.reverse(), ...lineNeed]
                                } else {
                                    lineNeed2.unshift()
                                    finalLine2Coordinates = [...lineNeed1.reverse(), ...lineNeed2]
                                }
                            } else if (isSamePoint(start, turf.point(lineNeed2[0]))) {
                                if (isSamePoint(stop, turf.point(lineNeed1[0]))) {
                                    const lineNeed = lineNeed1.reverse()
                                    lineNeed.unshift()
                                    finalLine2Coordinates = [...lineNeed2, ...lineNeed]
                                } else {
                                    lineNeed1.unshift()
                                    finalLine2Coordinates = [...lineNeed2, ...lineNeed1]
                                }
                            } else {
                                if (isSamePoint(stop, turf.point(lineNeed1[0]))) {
                                    const lineNeed = lineNeed1.reverse()
                                    lineNeed.unshift()
                                    finalLine2Coordinates = [...lineNeed2.reverse(), ...lineNeed]
                                } else {
                                    lineNeed1.unshift()
                                    finalLine2Coordinates = [...lineNeed2.reverse(), ...lineNeed1]
                                }
                            }
                            line2 = turf.lineString(finalLine2Coordinates)
                        }
                        // 3. 得出长短最短的那一条线(起点是上一个已有的点，终点是点击的点)
                        let shortestLineCoordinates
                        if (turf.length(line1, {units: 'miles'}) > turf.length(line2, {units: 'miles'})) {
                            shortestLineCoordinates = line2.geometry.coordinates
                        } else {
                            shortestLineCoordinates = line1.geometry.coordinates
                        }
                        // 4. 叠加点
                        // 不叠加起点，因为起点是上一个已有的点
                        for (let m = 1; m < shortestLineCoordinates.length; m++) {
                            const coordinate = shortestLineCoordinates[m];
                            state.polygon.updateCoordinate(`0.${state.currentVertexPosition}`, coordinate[0], coordinate[1])
                            state.polygon.updateCoordinate(`0.${++state.currentVertexPosition}`, coordinate[0], coordinate[1])
                        }
                    }
                }
            }

            if (stickToFeatures.length !== 0) {
                // 当前点
                const currentPoint = turf.point(currentCoordinate)
                for (let i = 0; i < stickToFeatures.length; i++) {
                    const otherFeature = stickToFeatures[i];
                    // 优先返回顶点
                    // 多面坐标
                    const multiPolygonCoordinates = otherFeature.geometry.coordinates;
                    for (let j = 0; j < multiPolygonCoordinates.length; j++) {
                        // 单面坐标
                        const polygonCoordinates = multiPolygonCoordinates[j]
                        for (let k = 0; k < polygonCoordinates.length; k++) {
                            // 外环或内环坐标
                            const coordinates = polygonCoordinates[k]
                            // 外环或内环
                            const ring = turf.lineString(coordinates);
                            for (let l = 0; l < coordinates.length - 1; l++) {
                                // 点坐标
                                const coordinate = coordinates[l]
                                if (getPixelBetweenTowPointsWithZoom(currentCoordinate, coordinate, zoomLevel) < minPixelAllow) {
                                    stickToMore(ring, coordinate);
                                    return {
                                        currentCoordinate: coordinate,
                                        // 是否在边界上
                                        onBoundary: true
                                    }
                                }
                            }

                            // 顶点不符合，则尝试返回外环或内环上距离最近的点
                            const nearestPoint = turf.nearestPointOnLine(ring, currentPoint);
                            const nearestPointCoordinates = nearestPoint.geometry.coordinates;
                            if (getPixelBetweenTowPointsWithZoom(currentCoordinate, nearestPointCoordinates, zoomLevel) < minPixelAllow) {
                                stickToMore(ring, nearestPointCoordinates)
                                return {
                                    currentCoordinate: nearestPointCoordinates,
                                    onBoundary: true
                                }
                            }
                        }
                    }
                }
            }
            return {
                currentCoordinate: currentCoordinate,
                onBoundary: false
            }
        }

        // 和自己有交叉
        const crossSelf = (polygonCoordinates, currentIndex, currentCoordinate) => {
            if (currentIndex > 2) {
                // 要判断当前点和第一个点, 上一个点, 3点组成的折线,
                // 他们和已有的多边形各个边都没有交叉, 才算和自己没有交叉
                const startCoordinate = polygonCoordinates[0]
                const endCoordinate = polygonCoordinates[currentIndex - 1]
                polygonCoordinates.shift()
                polygonCoordinates.pop()
                polygonCoordinates.pop()
                const currentLine = turf.lineString([startCoordinate, currentCoordinate, endCoordinate])
                const polygonLine = polygonCoordinates.length === 1 ? turf.point(polygonCoordinates[0]) : turf.lineString(polygonCoordinates)

                if (booleanIntersects(currentLine, polygonLine)) {
                    return true
                } else {
                    const startLine = turf.lineString([startCoordinate, polygonCoordinates[0]])
                    const endLine = turf.lineString([endCoordinate, polygonCoordinates[polygonCoordinates.length - 1]])
                    // 开始点到当前点的线
                    const startToCurrentPointLine = turf.lineString([startCoordinate, currentCoordinate])
                    // 当前点到结束点的线
                    const currentPointToEndLine = turf.lineString([currentCoordinate, endCoordinate])
                    return booleanIntersects(startLine, currentPointToEndLine) || booleanIntersects(startToCurrentPointLine, endLine)
                }
            }
            return false
        }

        return {
            PolygonStickTo,
            PolygonStickToEvents
        }
    }

    module.exports = {
        onload
    }
});
